package net.sansi.v3correctserver.service.photorec.rec;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.sansi.v3correctserver.entity.IntPoint;
import net.sansi.v3correctserver.entity.color.PhotoColor;
import net.sansi.v3correctserver.entity.image.RecImage;
import net.sansi.v3correctserver.util.CorrectUtil;

import java.util.List;

/**
 * Created by Fang
 * create time : 2024/11/4
 * IDE : IntelliJ IDEA
 * <p>
 * 图像识别基类
 */

@Getter
@Slf4j
public class RecBase {

    // 打屏 led 相片
    protected final RecImage ledPhoto;
    // 全暗相片
    protected final RecImage blackPhoto;
    // 边界围栏
    protected final List<IntPoint> fence;
    // 基准亮度
    protected double baseBright = 0;
    // 高光亮度
    protected double highBright = 0;

    /**
     * 构造函数
     *
     * @param ledPhoto   LED 相片
     * @param blackPhoto 黑色背景
     * @param fence      围栏
     */
    public RecBase(RecImage ledPhoto, RecImage blackPhoto, List<IntPoint> fence) {
        this.ledPhoto = ledPhoto;
        this.blackPhoto = blackPhoto;
        this.fence = fence;
    }


    protected double computeBaseBright() {
        double res = computeBaseBrightFromGradient();
        if (res == -1 || res == Double.MAX_VALUE) return computeBaseBrightFromMax();
        return res;
    }

    /**
     * 初始化基准亮度
     */
    protected double computeBaseBrightFromMax() {
        int w = getLedPhoto().getWidth(), h = getLedPhoto().getHeight();
        int l = w / 3, r = w - l, t = h / 3, b = h - t;
        int span = 3;
        double highBright = 0;
        for (int x = l; x < r; x += span) {
            for (int y = t; y < b; y += span) {
                if (!isInFence(x, y)) continue;
                double bri = getPixelBrightness(x, y);
                highBright = Math.max(highBright, bri);
            }
        }
        double res = Math.pow(highBright, 1.37 / 2);
        log.info("[use base bright] {} ({})", res, highBright);
        return res;
    }

    protected double computeBaseBrightFromGradient() {
        log.info("开始评估地平线海拔");
        int w = getLedPhoto().getWidth(), h = getLedPhoto().getHeight();
        int sx = w / 2, sy = h / 2;
        double min = -1, max = -1;
        for (int x = sx; x < sx + 100; x++) {
            for (int y = sy; y < sy + 100; y++) {
                double bri = getPixelBrightness(x, y);
                int m = isMinMax(x, y, bri);
                if (m > 0) max = Math.max(max, bri);
                else if (m < 0) min = Math.max(min, bri);
            }
        }
        double res = min + (max - min) * 1 / 100;
        log.info("[use base bright gradient] [min] {} [max] {} [baseBright] {}", min, max, res);
        return res;
    }


    private int isMinMax(int x, int y, double bri) {
        int count = 3;
        double left = 0, right = 0;
        for (int i = x - count; i <= x + count; i++) {
            if (i < x) left += getPixelBrightness(i, y);
            if (i > x) right += getPixelBrightness(i, y);
        }
        left /= count;
        right /= count;
        double top = 0, bottom = 0;
        for (int i = y - count; i <= y + count; i++) {
            if (i < y) top += getPixelBrightness(x, i);
            if (i > y) bottom += getPixelBrightness(x, i);
        }
        top /= count;
        bottom /= count;
        if (bri >= top && bri >= bottom && bri >= left && bri >= right) return 1;
        if (bri < top && bri < bottom && bri > left && bri > right) return -1;
        if (bri > top && bri > bottom && bri < left && bri < right) return -1;
        return 0;
    }

    /**
     * 获取相片像素颜色
     *
     * @param x x坐标
     * @param y y坐标
     * @return 颜色
     */
    protected PhotoColor getPixelColor(int x, int y) {
        PhotoColor pColor = getLedPhoto().getPhotoColor(x, y);
        if (blackPhoto == null) return pColor;
        PhotoColor bColor = blackPhoto.getPhotoColor(x, y);
        return pColor.minus(bColor);
    }

    /**
     * 获取基准亮度
     *
     * @param x x坐标
     * @param y y坐标
     * @return 基准亮度
     */
    protected double getPixelBrightness(int x, int y) {
        double pBri = getLedPhoto().getBrightness(x, y);
        if (blackPhoto == null) return pBri;
        return pBri - blackPhoto.getBrightness(x, y);
    }


    /**
     * 是否在相片内
     *
     * @param x x坐标
     * @param y y坐标
     * @return 是否在相片内
     */
    protected boolean isInPhoto(int x, int y) {
        return x >= 0 && x < getLedPhoto().getWidth() && y >= 0 && y < getLedPhoto().getHeight();
    }

    /**
     * 是否在边界内
     *
     * @param x x坐标
     * @param y y坐标
     * @return 是否在边界内
     */
    protected boolean isInFence(int x, int y) {
        if (fence == null || fence.isEmpty()) return isInPhoto(x, y);
        return CorrectUtil.isInFence(x, y, fence);
    }

    /**
     * 计算在某个方向上的灯点平均距离
     *
     * @param p1  点1
     * @param p2  点2
     * @param dir 方向
     * @return 平均距离
     */
    protected int computeLedDistance(IntPoint p1, IntPoint p2, int dir) {
        if (dir == 0 || dir == 2) return Math.abs(p1.getY() - p2.getY());
        return Math.abs(p1.getX() - p2.getX());
    }

}
